﻿using System.Collections;
using System.Collections.Generic;

namespace NRank.Common
{
	/// <summary>
	/// Helper class for rank join algorithms providing caching collection and contains 
	/// maxrank and minrank of seen items.
	/// </summary>
	/// <typeparam name="T">Type of source items</typeparam>
	public class RankJoinSource<T> : IEnumerable<IRanked<T>>, IBoundHolder
	{
		/// <summary>
		/// Original source
		/// </summary>
		private readonly IEnumerable<IRanked<T>> originalSource;

		/// <summary>
		/// Creates instance of RankJoinSource class
		/// </summary>
		/// <param name="originalSource">Original items to be cached</param>
		public RankJoinSource(IEnumerable<IRanked<T>> originalSource)
		{
			this.originalSource = originalSource;
			this.SeenItems = new List<IRanked<T>>();
		}

		/// <summary>
		/// Determines wheather first item has been read
		/// </summary>
		private bool hasReadFirst;

		/// <summary>
		/// Enumeration of items
		/// </summary>
		/// <returns></returns>
		private IEnumerable<IRanked<T>> GetItems()
		{
			hasReadFirst = false;
			this.MaxRank = double.MaxValue;
			this.MinRank = double.MaxValue;

			this.SeenItems.Clear();

			foreach (var item in originalSource)
			{
				// update bounds
				if (!hasReadFirst)
				{
					this.MaxRank = item.Rank;
					hasReadFirst = true;
				}

				this.MinRank = item.Rank;

				// cache item
				SeenItems.Add(item);

				// return item
				yield return item;
			}
		}

		/// <summary>
		/// Items' enumerator
		/// </summary>
		/// <returns>Items' enumerator</returns>
		public IEnumerator<IRanked<T>> GetEnumerator()
		{
			return GetItems().GetEnumerator();
		}

		/// <summary>
		/// Nongeneric items' enumerator
		/// </summary>
		/// <returns>Nongeneric items' enumerator</returns>
		IEnumerator IEnumerable.GetEnumerator()
		{
			return GetEnumerator();
		}

		/// <summary>
		/// Minimal rank
		/// </summary>
		public double MinRank { get; private set; }
		
		/// <summary>
		/// Maximal rank
		/// </summary>
		public double MaxRank {get; private set;}

		/// <summary>
		/// So far seen items
		/// </summary>
		public List<IRanked<T>> SeenItems { get; private set; }
	}
}